作用域指的是變數或函式在程式碼中的有效範圍,有全域和區域兩種,全域的作用域是文件中的全部程式碼,區域的作用域只在一個block裡面
作用域鏈(scope chain)
在原本的作用域裡沒有指定的變數,於是繼續往父層尋找,直到找到為止,這就是scope chain
什麼是 hoisting?
var, let, const差別
console.log((0.1+0.2) === 0.3)的結果是什麼?
結果會是falsy,因為電腦是二進位制,電腦表示精準度有限,所以實際上小數點後面會有誤差,0.1加上0.2之後不會「剛好」等於0.3
可以使用toFixed解決,設定精確到小數第一位
(0.1+0.2).toFixed(1)
falsy value
falsy value是指一個不完全是false的值,但當我們嘗試將它轉變成布林值時,我們會得到false。在JavaScript中,有5個falsy values,分別是0、空字串(empty string)、NaN、null及undefined。
弱型別是甚麼
js是弱型別的語言,在運算前會進行型別轉換,假設轉換後發現有字串,就會進行字串的相加,否則就是一般的數字相加。若想都以數字計算,假設有浮點數的話,可用Number(),或假設是字串和數字混雜的話,可用parseInt();另一方面,JS在減法時會將字串轉為數值,如果是 const str = "3"
減 const num = 1
的話,會以數字做計算。Typescript是用來解決弱型別的問題
== 與===的差別在哪
兩個等號是寬鬆相等,在比較時會做型別轉換,假設我們把字串1==數字1,字串會被轉換成數字,最後回傳true。
三個等號不會進行型別轉換,所以如果上面兩個值去做嚴格相等比較,最後回傳false
通常不建議用寬鬆相等,可能因為型別轉換造成預期外的結果。
const a = 1;
console.log(a);
const outer = () => {
let a = 1;
const inner = () => {
a += 1;
console.log(a);
};
return inner;
};
const result = outer();
result(1);//output 2
result(1);//output 3
當我們呼叫 result() 時,實際上是在執行了 inner() 函式,而 inner 函式內部的 a 變數形成了一個閉包(Closure)。每次呼叫 result(),都能繼續存取並修改 a 的值,且 a 的值不會重新初始化。這是因為 a 變數被 inner 函式的作用域所捕獲,使得在每次呼叫 result() 時都能累加之前a的值。
同步與非同步、promise是什麼?
同步就是按順序執行,一行程式碼執行完才會執行下一行的意思,但它有個問題,如果今天讀取檔案 (例如是背景圖片) 的程式碼在最前面,它採用同步執行而且花費超久的時間,使用者可能等很久頁面還是一片空白,因為處理事件的流程被「卡住」了
非同步就是不一定要按照順序執行,如果 JS 沒有「非同步」的特性,網頁可能會跑兩行字,然後去拿圖片資源,當圖片沒有完全被載入,圖片後面的文字也無法出現,這對於使用者體驗很糟。有了JS「非同步」的特性,網頁中的文字會先完全顯示出來,等圖片被完全載入後再顯示出來,在圖片出現之前,使用者能閱讀網頁中的所有文字內容。
Promise是用來解決非同步事件處理,確保非同步事件完成後才繼續執行其他程式碼,在執行過程中可以看到三種狀態,分別是pending(進行中), fulfilled(已成功),跟rejected(已失敗),可以用.then()和.catch()語法去實作,更進階的語法是finally(),代表無論promise狀態無論fulfilled或rejected,都會執行finally()裡面的程式碼,如果網站裡有設置在向後端fetch資料時出現loading效果,使用finally()能確保無論資料有無回傳,都會關閉loading效果
為何有callback hell、如何解決?
async await 是什麼?
async function getData(){
try{
const res = await fetch("example-url",{method:"GET"});
const data = await res.json();
console.log(data);
}catch(err){
console.log(err)
}
}
Axios是什麼?使用它的優點為何?
axios.get("url")
,傳資料給server不需用JSON.stringify()
,從server接收資料也不需要用json()
就能使用//確定已經跑過 npm i axios
const axios = require('axios');
axios.get('url')
.then(res=>{
console.log('Data', res.data)
}).catch(err=>{
console.log(err)
})
如果是基本型別 (Primitive type),原始變數「不會」跟著複製變數的改變而變,表現出的行為是 pass by value。
如果是物件型別,且僅針對物件的內容做改變,原始變數「會」跟著複製變數的改變而有所不同,表現出的行為結果就是 pass by reference。傳參考表示變數指向的是同一個記憶體位置,修改其值就會導致共享該記憶體的其他變數一起被修改
const obj1 = { id: 1 };
const obj2 = obj1;
console.log(obj1 === obj2); //true 兩個變數共享同個記憶體位置
但要注意以下情境
let c = {name:'Sally'}
let d;
d=c;
c={name:'Angela'}
console.log(c); // {name:'Angela'}
console.log(d); // {name:'Sally'}
當我們將一個新的物件賦值給c時,實際上是創建了一個新的物件,並將它的參考賦值給了c。此時,c和d已經沒有任何關係了
在 JavaScript 中,多數的預設方法和運算子進行的通常是淺拷貝。然而,有一些方法並不是 JavaScript 核心語言的一部分,而是通過函式庫或框架提供的,可以執行深拷貝操作
const originalObj = { a: 1, b: { c: 2 } };
const deepCopiedObj = JSON.parse(JSON.stringify(originalObj));
originalObj.b.c = 100;
console.log(deepCopiedObj); // Output: { a: 1, b: { c: 2 } }
深層複製需要耗費更多資源,不一定有必要。對於大多數情況來說,使用展開運算子進行淺層複製已經很夠了
展開運算子spread(...)
複製現有陣列:如果你想要創建一個現有陣列的複製,你可以使用展開運算符將現有陣列的元素展開到一個新陣列中。例如,const newArray = [...oldArray] 將創建一個新陣列,運用的是淺拷貝。
合併陣列:如果你想要通過合併兩個或更多陣列的元素來創建一個新陣列,你可以使用展開運算符將每個陣列的元素展開到一個新陣列中。例如,const newArray = [...array1, ...array2] 將創建一個新陣列,其中包含 array1 的元素,然後是 array2 的元素。
將元素添加到陣列:如果你想要將一個或多個元素添加到現有陣列並創建一個新陣列,你可以使用展開運算符將現有陣列的元素展開到一個新陣列中,然後添加新元素。例如,const newArray = [...oldArray, newItem1, newItem2]
以上若有任何錯誤,都歡迎留言給我,謝謝